package xueqiu

import (
	"github.com/duke-git/lancet/v2/slice"
	"github.com/imroc/req/v3"
	"github.com/pkg/errors"
	"stock/server/dao"
	"strconv"
	"strings"
)

type codeInfoGroupItem struct {
	ID          int    `json:"id"`
	Name        string `json:"name"`
	OrderID     int    `json:"order_id"`
	Category    int    `json:"category"`
	Include     bool   `json:"include"`
	SymbolCount int    `json:"symbol_count"`
	Type        int    `json:"type"`
	CreatedAt   int64  `json:"created_at"`
	UpdatedAt   int64  `json:"updated_at"`
}

type codeInfoStockResp struct {
	Data struct {
		Pid      int `json:"pid"`
		Category int `json:"category"`
		Stocks   []struct {
			Symbol      string `json:"symbol"`
			Name        string `json:"name"`
			Type        int    `json:"type"`
			Remark      string `json:"remark"`
			Exchange    string `json:"exchange"`
			Created     int64  `json:"created"`
			Watched     int64  `json:"watched"`
			Category    int    `json:"category"`
			Marketplace string `json:"marketplace"`
		} `json:"stocks"`
	} `json:"data"`
	ErrorCode        int    `json:"error_code"`
	ErrorDescription string `json:"error_description"`
}

type codeInfoGroupResp struct {
	Data struct {
		Cubes       []codeInfoGroupItem `json:"cubes"`       // 自选组合
		Funds       []codeInfoGroupItem `json:"funds"`       // 自选基金
		Stocks      []codeInfoGroupItem `json:"stocks"`      // 自选股票
		MutualFunds []codeInfoGroupItem `json:"mutualFunds"` // 未知
	} `json:"data"`
	ErrorCode        int    `json:"error_code"`
	ErrorDescription string `json:"error_description"`
}

type Xueqiu struct {
	token    string
	codeInfo []dao.StockInfo
}

func (o *Xueqiu) Group() ([]string, map[string][]string, error) {
	var group []string
	var groupMap = make(map[string][]string)
	for _, item := range o.codeInfo {
		for _, groupName := range item.Group {
			if _, ok := groupMap[groupName]; ok {
				groupMap[groupName] = append(groupMap[groupName], item.Code)
				continue
			}

			groupMap[groupName] = []string{item.Code}
			group = append(group, groupName)
		}
	}
	return group, groupMap, nil
}

func (o *Xueqiu) Open(opt any) (db dao.IStockDB, err error) {
	// 登录雪球
	o.token = "dd29069564369860ceaf85c547e802e9b3778f8f"
	return o, nil
}

func (o *Xueqiu) CodeInfo() ([]dao.StockInfo, map[string]dao.StockInfo, error) {
	req.DevMode()
	// 获取分组信息
	body, err := req.DefaultClient().R().SetQueryParam("system", "true").
		SetHeader("Cookie", "xq_a_token="+o.token).
		Get("https://stock.xueqiu.com/v5/stock/portfolio/list.json")
	if err != nil {
		return nil, nil, errors.WithMessage(err, "请求雪球股票信息失败")
	}

	var resp codeInfoGroupResp
	err = body.UnmarshalJson(&resp)
	if err != nil {
		return nil, nil, errors.WithMessage(err, "解析雪球股票信息失败")
	}

	// 获取分组股票信息
	var stockMap = make(map[string]*dao.StockInfo)
	var data []dao.StockInfo
	allCodeInfoMap := make(map[string]dao.StockInfo)
	for _, stock := range resp.Data.Stocks {
		// 屏蔽默认分组, “全部”不能屏蔽
		if slice.Contain([]string{"沪深", "美股", "港股", "模拟"}, stock.Name) {
			continue
		}
		listBody, err := req.DefaultClient().R().SetQueryParam("size", "1000").
			SetHeader("Cookie", "xq_a_token="+o.token).
			SetQueryParam("category", "1").
			SetQueryParam("pid", strconv.Itoa(stock.ID)).
			Get("https://stock.xueqiu.com/v5/stock/portfolio/stock/list.json")
		if err != nil {
			return nil, nil, errors.WithMessage(err, "请求雪球分组股票信息失败")
		}

		var listResp codeInfoStockResp
		err = listBody.UnmarshalJson(&listResp)
		if err != nil {
			return nil, nil, errors.WithMessage(err, "解析雪球分组股票信息失败")
		}

		for _, stockSub := range listResp.Data.Stocks {
			code := strings.ToLower(stockSub.Symbol)
			if m, ok := stockMap[code]; ok {
				if stock.Name == "全部" {
					allCodeInfoMap[code] = *m
					continue
				}
				m.Group = append(m.Group, stock.Name)
				continue
			}

			item := dao.StockInfo{
				Code:         code,
				Title:        stockSub.Name,
				Abbreviation: stockSub.Name,
				Exchange:     strings.ToLower(stockSub.Exchange),
				Type:         o.toType(stockSub.Type),
			}

			// 过滤默认分组
			if stock.Name != "全部" {
				item.Group = []string{stock.Name}
			}
			data = append(data, item)

			stockMap[code] = &data[len(data)-1]
		}
	}

	o.codeInfo = data

	return data, allCodeInfoMap, nil
}

func (o *Xueqiu) toType(tye int) string {
	switch tye {
	case 11:
		return "stock"
	case 12:
		return "index"
	case 13:
		return "ETF"
	default:
		return "nil"
	}
}
